﻿/**********************************************************************************
Program:       chinaweathergadget

License:       GNU/GPL version 3

File:          NewButton.cs

Description:   重写的 Button 类

Author:        Guangyu Liu （lgy0417@gmail.com）

Environment:   .NET Framework v2.0 or later version

Usage/Example: README_chs.txt

Last Modified: 2010.08.12

***********************************************************************************/


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;


namespace PrettifiedControls
{
	namespace PrettifiedButton
	{
		public class ButtonNew : Button
		{
			private Color _baseColor = Color.FromArgb(51, 161, 224);
			private ControlState _controlState;
			private int _imageWidth = 18;
			private RoundStyle _roundStyle = RoundStyle.All;
			private int _radius = 8;

			public ButtonNew() : base()
			{
				SetStyle(
					ControlStyles.UserPaint |
					ControlStyles.AllPaintingInWmPaint |
					ControlStyles.OptimizedDoubleBuffer |
					ControlStyles.ResizeRedraw |
					ControlStyles.SupportsTransparentBackColor, true);
			}

			[DefaultValue(typeof(Color),"51, 161, 224")]
			public Color BaseColor
			{
				get { return _baseColor; }
				set
				{
					_baseColor = value;
					base.Invalidate();
				}
			}

			[DefaultValue(18)]
			public int ImageWidth
			{
				get { return _imageWidth; }
				set
				{
					if (value != _imageWidth)
					{

						_imageWidth = value < 12 ? 12 : value;
						base.Invalidate();
					}
				}
			}

			[DefaultValue(typeof(RoundStyle), "1")]
			public RoundStyle RoundStyle
			{
				get { return _roundStyle; }
				set
				{
					if (_roundStyle != value)
					{
						_roundStyle = value;
						base.Invalidate();
					}
				}
			}

			[DefaultValue(8)]
			public int Radius
			{
				get { return _radius; }
				set
				{
					if (_radius != value)
					{
						_radius = value < 4 ? 4 : value;
						base.Invalidate();
					}
				}
			}

			internal ControlState ControlState
			{
				get { return _controlState; }
				set
				{
					if (_controlState != value)
					{
						_controlState = value;
						base.Invalidate();
					}
				}
			}

			protected override void OnMouseEnter(EventArgs e)
			{
				base.OnMouseEnter(e);
				ControlState = ControlState.Hover;
			}

			protected override void OnMouseLeave(EventArgs e)
			{
				base.OnMouseLeave(e);
				ControlState = ControlState.Normal;
			}

			protected override void OnMouseDown(MouseEventArgs e)
			{
				base.OnMouseDown(e);
				if (e.Button == MouseButtons.Left && e.Clicks == 1)
				{
					ControlState = ControlState.Pressed;
				}
			}

			protected override void OnMouseUp(MouseEventArgs e)
			{
				base.OnMouseUp(e);
				if (e.Button == MouseButtons.Left && e.Clicks == 1)
				{
					if (ClientRectangle.Contains(e.Location))
					{
						ControlState = ControlState.Hover;
					}
					else
					{
						ControlState = ControlState.Normal;
					}
				}
			}

			protected override void OnPaint(PaintEventArgs e)
			{
				base.OnPaint(e);
				base.OnPaintBackground(e);

				Graphics g = e.Graphics;
				Rectangle imageRect;
				Rectangle textRect;

				CalculateRect(out imageRect, out textRect);
				g.SmoothingMode = SmoothingMode.AntiAlias;

				Color baseColor;
				Color borderColor;
				Color innerBorderColor = Color.FromArgb(200, 255, 255, 255);;

				if (Enabled)
				{
					switch (ControlState)
					{
						case ControlState.Hover:
							baseColor = GetColor(_baseColor, 0, -13, -8, -3);
							borderColor = _baseColor;
							break;
						case ControlState.Pressed:
							baseColor = GetColor(_baseColor, 0, -35, -24, -9);
							borderColor = _baseColor;
							break;
						default:
							baseColor = _baseColor;
							borderColor = _baseColor;
							break;
					}
				}
				else
				{
					baseColor = SystemColors.ControlDark;
					borderColor = SystemColors.ControlDark;
				}

				RenderBackgroundInternal(
					g,
					ClientRectangle,
					baseColor,
					borderColor,
					innerBorderColor,
					RoundStyle,
					Radius,
					0.35f,
					true,
					true,
					LinearGradientMode.Vertical);

				if(Image != null)
				{
					g.InterpolationMode = InterpolationMode.HighQualityBilinear;
					g.DrawImage(
						Image,
						imageRect,
						0,
						0,
						Image.Width,
						Image.Height,
						GraphicsUnit.Pixel);
				}

				TextRenderer.DrawText(
					g,
					Text,
					Font,
					textRect,
					ForeColor,
					GetTextFormatFlags(TextAlign,RightToLeft == RightToLeft.Yes));
			}

			private void CalculateRect(
				out Rectangle imageRect, out Rectangle textRect)
			{
				imageRect = Rectangle.Empty;
				textRect = Rectangle.Empty;
				if (Image == null)
				{
					textRect = new Rectangle(
					   2,
					   0,
					   Width - 4,
					   Height);
					return;
				}
				switch (TextImageRelation)
				{
					case TextImageRelation.Overlay:
						imageRect = new Rectangle(
							2, 
							(Height - ImageWidth) / 2, 
							ImageWidth, 
							ImageWidth);
						textRect = new Rectangle(
							2,
							0,
							Width - 4,
							Height);
						break;
					case TextImageRelation.ImageAboveText:
						imageRect = new Rectangle(
							(Width - ImageWidth) / 2,
							2,
							ImageWidth,
							ImageWidth);
						textRect = new Rectangle(
							2,
							imageRect.Bottom,
							Width,
							Height - imageRect.Bottom - 2);
						break;
					case TextImageRelation.ImageBeforeText:
						imageRect = new Rectangle(
							2,
							(Height - ImageWidth) / 2,
							ImageWidth,
							ImageWidth);
						textRect = new Rectangle(
							imageRect.Right + 2,
							0,
							Width - imageRect.Right - 4,
							Height);
						break;
					case TextImageRelation.TextAboveImage:
						imageRect = new Rectangle(
							(Width - ImageWidth) / 2,
							Height - ImageWidth - 2,
							ImageWidth,
							ImageWidth);
						textRect = new Rectangle(
							0,
							2,
							Width,
							Height - imageRect.Y - 2);
						break;
					case TextImageRelation.TextBeforeImage:
						imageRect = new Rectangle(
							Width - ImageWidth - 2,
							(Height - ImageWidth) / 2,
							ImageWidth,
							ImageWidth);
						textRect = new Rectangle(
							2,
							0,
							imageRect.X - 2,
							Height);
						break;
				}

				if (RightToLeft == RightToLeft.Yes)
				{
					imageRect.X = Width - imageRect.Right;
					textRect.X = Width - textRect.Right;
				}
			}

			internal void RenderBackgroundInternal(
			   Graphics g,
			   Rectangle rect,
			   Color baseColor,
			   Color borderColor,
			   Color innerBorderColor,
			   RoundStyle style,
			   int roundWidth,
			   float basePosition,
			   bool drawBorder,
			   bool drawGlass,
			   LinearGradientMode mode)
			{
				if (drawBorder)
				{
					rect.Width--;
					rect.Height--;
				}

				using (LinearGradientBrush brush = new LinearGradientBrush(
					rect, Color.Transparent, Color.Transparent, mode))
				{
					Color[] colors = new Color[4];
					colors[0] = GetColor(baseColor, 0, 35, 24, 9);
					colors[1] = GetColor(baseColor, 0, 13, 8, 3);
					colors[2] = baseColor;
					colors[3] = GetColor(baseColor, 0, 68, 69, 54);

					ColorBlend blend = new ColorBlend();
					blend.Positions = new float[] { 0.0f, basePosition, basePosition + 0.05f, 1.0f };
					blend.Colors = colors;
					brush.InterpolationColors = blend;
					if (style != RoundStyle.None)
					{
						using (GraphicsPath path =
							GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
						{
							g.FillPath(brush, path);
						}

						if (baseColor.A > 80)
						{
							Rectangle rectTop = rect;

							if (mode == LinearGradientMode.Vertical)
							{
								rectTop.Height = (int)(rectTop.Height * basePosition);
							}
							else
							{
								rectTop.Width = (int)(rect.Width * basePosition);
							}
							using (GraphicsPath pathTop = GraphicsPathHelper.CreatePath(
								rectTop, roundWidth, RoundStyle.Top, false))
							{
								using (SolidBrush brushAlpha =
									new SolidBrush(Color.FromArgb(80, 255, 255, 255)))
								{
									g.FillPath(brushAlpha, pathTop);
								}
							}
						}

						if (drawGlass)
						{
							RectangleF glassRect = rect;
							if (mode == LinearGradientMode.Vertical)
							{
								glassRect.Y = rect.Y + rect.Height * basePosition;
								glassRect.Height = (rect.Height - rect.Height * basePosition) * 2;
							}
							else
							{
								glassRect.X = rect.X + rect.Width * basePosition;
								glassRect.Width = (rect.Width - rect.Width * basePosition) * 2;
							}
							DrawGlass(g, glassRect, 170, 0);
						}

						if (drawBorder)
						{
							using (GraphicsPath path =
								GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
							{
								using (Pen pen = new Pen(borderColor))
								{
									g.DrawPath(pen, path);
								}
							}

							rect.Inflate(-1, -1);
							using (GraphicsPath path =
								GraphicsPathHelper.CreatePath(rect, roundWidth, style, false))
							{
								using (Pen pen = new Pen(innerBorderColor))
								{
									g.DrawPath(pen, path);
								}
							}
						}
					}
					else
					{
						g.FillRectangle(brush, rect);
						if (baseColor.A > 80)
						{
							Rectangle rectTop = rect;
							if (mode == LinearGradientMode.Vertical)
							{
								rectTop.Height = (int)(rectTop.Height * basePosition);
							}
							else
							{
								rectTop.Width = (int)(rect.Width * basePosition);
							}
							using (SolidBrush brushAlpha =
								new SolidBrush(Color.FromArgb(80, 255, 255, 255)))
							{
								g.FillRectangle(brushAlpha, rectTop);
							}
						}

						if (drawGlass)
						{
							RectangleF glassRect = rect;
							if (mode == LinearGradientMode.Vertical)
							{
								glassRect.Y = rect.Y + rect.Height * basePosition;
								glassRect.Height = (rect.Height - rect.Height * basePosition) * 2;
							}
							else
							{
								glassRect.X = rect.X + rect.Width * basePosition;
								glassRect.Width = (rect.Width - rect.Width * basePosition) * 2;
							}
							DrawGlass(g, glassRect, 200, 0);
						}

						if (drawBorder)
						{
							using (Pen pen = new Pen(borderColor))
							{
								g.DrawRectangle(pen, rect);
							}

							rect.Inflate(-1, -1);
							using (Pen pen = new Pen(innerBorderColor))
							{
								g.DrawRectangle(pen, rect);
							}
						}
					}
				}
			}

			private void DrawGlass(
				Graphics g, RectangleF glassRect, int alphaCenter, int alphaSurround)
			{
				DrawGlass(g, glassRect, Color.White, alphaCenter, alphaSurround);
			}

			private void DrawGlass(
				Graphics g,
				RectangleF glassRect,
				Color glassColor,
				int alphaCenter,
				int alphaSurround)
			{
				using (GraphicsPath path = new GraphicsPath())
				{
					path.AddEllipse(glassRect);
					using (PathGradientBrush brush = new PathGradientBrush(path))
					{
						brush.CenterColor = Color.FromArgb(alphaCenter, glassColor);
						brush.SurroundColors = new Color[] { 
							Color.FromArgb(alphaSurround, glassColor) };
						brush.CenterPoint = new PointF(
							glassRect.X + glassRect.Width / 2,
							glassRect.Y + glassRect.Height / 2);
						g.FillPath(brush, path);
					}
				}
			}

			private Color GetColor(Color colorBase, int a, int r, int g, int b)
			{
				int a0 = colorBase.A;
				int r0 = colorBase.R;
				int g0 = colorBase.G;
				int b0 = colorBase.B;

				if (a + a0 > 255) { a = 255; } else { a = Math.Max(a + a0, 0); }
				if (r + r0 > 255) { r = 255; } else { r = Math.Max(r + r0, 0); }
				if (g + g0 > 255) { g = 255; } else { g = Math.Max(g + g0, 0); }
				if (b + b0 > 255) { b = 255; } else { b = Math.Max(b + b0, 0); }

				return Color.FromArgb(a, r, g, b);
			}

			internal static TextFormatFlags GetTextFormatFlags(
				ContentAlignment alignment,
				bool rightToleft)
			{
				TextFormatFlags flags = TextFormatFlags.WordBreak |
					TextFormatFlags.SingleLine;
				if (rightToleft)
				{
					flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
				}

				switch (alignment)
				{
					case ContentAlignment.BottomCenter:
						flags |= TextFormatFlags.Bottom | TextFormatFlags.HorizontalCenter;
						break;
					case ContentAlignment.BottomLeft:
						flags |= TextFormatFlags.Bottom | TextFormatFlags.Left;
						break;
					case ContentAlignment.BottomRight:
						flags |= TextFormatFlags.Bottom | TextFormatFlags.Right;
						break;
					case ContentAlignment.MiddleCenter:
						flags |= TextFormatFlags.HorizontalCenter |
							TextFormatFlags.VerticalCenter;
						break;
					case ContentAlignment.MiddleLeft:
						flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.Left;
						break;
					case ContentAlignment.MiddleRight:
						flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.Right;
						break;
					case ContentAlignment.TopCenter:
						flags |= TextFormatFlags.Top | TextFormatFlags.HorizontalCenter;
						break;
					case ContentAlignment.TopLeft:
						flags |= TextFormatFlags.Top | TextFormatFlags.Left;
						break;
					case ContentAlignment.TopRight:
						flags |= TextFormatFlags.Top | TextFormatFlags.Right;
						break;
				}
				return flags;
			}
		}
		
		
		public static class GraphicsPathHelper
		{
			/// <summary>
			/// 建立带有圆角样式的路径。
			/// </summary>
			/// <param name="rect">用来建立路径的矩形。</param>
			/// <param name="_radius">圆角的大小。</param>
			/// <param name="style">圆角的样式。</param>
			/// <param name="correction">是否把矩形长宽减 1,以便画出边框。</param>
			/// <returns>建立的路径。</returns>
			public static GraphicsPath CreatePath(
				Rectangle rect, int radius, RoundStyle style, bool correction)
			{
				GraphicsPath path = new GraphicsPath();
				int radiusCorrection = correction ? 1 : 0;
				switch (style)
				{
					case RoundStyle.None:
						path.AddRectangle(rect);
						break;
					case RoundStyle.All:
						path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
						path.AddArc(
							rect.Right - radius - radiusCorrection,
							rect.Y,
							radius,
							radius,
							270,
							90);
						path.AddArc(
							rect.Right - radius - radiusCorrection,
							rect.Bottom - radius - radiusCorrection,
							radius,
							radius, 0, 90);
						path.AddArc(
							rect.X,
							rect.Bottom - radius - radiusCorrection,
							radius,
							radius,
							90,
							90);
						break;
					case RoundStyle.Left:
						path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
						path.AddLine(
							rect.Right - radiusCorrection, rect.Y,
							rect.Right - radiusCorrection, rect.Bottom - radiusCorrection);
						path.AddArc(
							rect.X,
							rect.Bottom - radius - radiusCorrection,
							radius,
							radius,
							90,
							90);
						break;
					case RoundStyle.Right:
						path.AddArc(
							rect.Right - radius - radiusCorrection,
							rect.Y,
							radius,
							radius,
							270,
							90);
						path.AddArc(
						   rect.Right - radius - radiusCorrection,
						   rect.Bottom - radius - radiusCorrection,
						   radius,
						   radius,
						   0,
						   90);
						path.AddLine(rect.X, rect.Bottom - radiusCorrection, rect.X, rect.Y);
						break;
					case RoundStyle.Top:
						path.AddArc(rect.X, rect.Y, radius, radius, 180, 90);
						path.AddArc(
							rect.Right - radius - radiusCorrection,
							rect.Y,
							radius,
							radius,
							270,
							90);
						path.AddLine(
							rect.Right - radiusCorrection, rect.Bottom - radiusCorrection,
							rect.X, rect.Bottom - radiusCorrection);
						break;
					case RoundStyle.Bottom:
						path.AddArc(
							rect.Right - radius - radiusCorrection,
							rect.Bottom - radius - radiusCorrection,
							radius,
							radius,
							0,
							90);
						path.AddArc(
							rect.X,
							rect.Bottom - radius - radiusCorrection,
							radius,
							radius,
							90,
							90);
						path.AddLine(rect.X, rect.Y, rect.Right - radiusCorrection, rect.Y);
						break;
				}
				path.CloseFigure();

				return path;
			}
		}
		
		
		internal enum ControlState
		{
			/// <summary>
			///  正常。
			/// </summary>
			Normal,
			/// <summary>
			/// 鼠标进入。
			/// </summary>
			Hover,
			/// <summary>
			/// 鼠标按下。
			/// </summary>
			Pressed,
			/// <summary>
			/// 获得焦点。
			/// </summary>
			Focused,
		}
		
		
		public enum RoundStyle
		{
			/// <summary>
			/// 四个角都不是圆角。
			/// </summary>
			None = 0,
			/// <summary>
			/// 四个角都为圆角。
			/// </summary>
			All = 1,
			/// <summary>
			/// 左边两个角为圆角。
			/// </summary>
			Left = 2,
			/// <summary>
			/// 右边两个角为圆角。
			/// </summary>
			Right = 3,
			/// <summary>
			/// 上边两个角为圆角。
			/// </summary>
			Top = 4,
			/// <summary>
			/// 下边两个角为圆角。
			/// </summary>
			Bottom = 5
		}
	}
}



/* 原始版权信息予以保留：
 * 作者：Starts_2000
 * 日期：2009-07-31
 * 网站：http://www.csharpwin.com CS 程序员之窗。
 * 你可以免费使用或修改以下代码，但请保留版权信息。
 * 具体请查看 CS程序员之窗开源协议（http://www.csharpwin.com/csol.html）。
 */
